#include "config.h"

#define DBG_SUBSYS S_LIBCONTROL

#include "lich_api.h"
#include "../chunk/chunk_cleanup.h"

int __table1_get_table_proto_l1(table1_t *table1, table_proto_t **_table, const chkid_t *top_chkid) {
        int ret, idx;
        table_proto_t *table_proto;

        YASSERT(top_chkid->type == __VOLUME_CHUNK__);

        *_table = NULL;

        idx = top_chkid->idx;
        if (idx == 0) {
                table_proto = table1->table_proto;
        } else {
                // TODO
                if (unlikely(idx > table1->ext_info.chunk_count)) {
                        ret = ENOENT;
                        GOTO(err_ret, ret);
                }

                if (unlikely(table1->ext[idx - 1] == NULL)) {
                        ret = ENOENT;
                        GOTO(err_ret, ret);
                }

                table_proto = table1->ext[idx - 1];
        }

        *_table = table_proto;
        return 0;
err_ret:
        return ret;
}

int __table1_update_table_proto(const table1_t *table1, table_proto_t *table_proto,
                                const chkinfo_t *new_chkinfo, const chkstat_t *new_chkstat)
{
        YASSERT(new_chkinfo != NULL);

        chkinfo_t *chkinfo = table_proto->chkinfo;
        chkstat_t *chkstat = table_proto->chkstat;

        chunk_cleanup_compare(table1->pool, &table1->chkid, chkinfo, new_chkinfo);
        
        //YASSERT(chkinfo->repnum == new_chkinfo->repnum);
        memcpy(chkinfo, new_chkinfo, CHKINFO_SIZE(new_chkinfo->repnum));
        if (new_chkstat) {
                memcpy(chkstat, new_chkstat, CHKSTAT_SIZE(new_chkinfo->repnum));
        }

        return 0;
}

int __table1_update_ext_info(table1_t *table1, const chkid_t *chkid,
                             const chkinfo_t *new_chkinfo, const chkstat_t *new_chkstat) {
        int ret;
        table_proto_t *table_proto;
        table1_ext_info_t ext_info;

        YASSERT(chkid->type == __VOLUME_CHUNK__ && chkid->idx > 0);

        memcpy(&ext_info, &table1->ext_info, sizeof(table1_ext_info_t));
        if (ext_info.chunk_count < chkid->idx) {
                ext_info.chunk_count = chkid->idx;
        }
        memcpy(&ext_info.chkinfo_array[chkid->idx - 1], new_chkinfo, CHKINFO_SIZE(new_chkinfo->repnum));

        table_proto = table1->table_proto;
        ret = table_proto->setinfo(table_proto, &ext_info, sizeof(table1_ext_info_t), TABLE_PROTO_BNO_EXT);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        memcpy(&table1->ext_info, &ext_info, sizeof(table1_ext_info_t));

        // update table proto
        table_proto = table1->ext[chkid->idx - 1];
        if (table_proto) {
                __table1_update_table_proto(table1, table_proto, new_chkinfo, new_chkstat);
        }

        return 0;
err_ret:
        return ret;
}

int __table1_chunk_newinfo(table1_t *table1, const chkid_t *chkid, chkinfo_t *chkinfo)
{
        int ret;
        diskid_t diskid[LICH_REPLICA_MAX];
        int repnum, repmin;

        repnum = table1->fileinfo.repnum_sys ? gloconf.metadata_replica : table1->fileinfo.repnum_sys;
        repmin = cluster_is_solomode()? 1 : LICH_REPLICA_MIN;
        
        ret = dispatch_newdisk(diskid, &repnum, repmin, table1->pool, NULL, 0, 0);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        memset(chkinfo, 0x0, sizeof(*chkinfo));
        diskid2loc(chkinfo->diskid, diskid, repnum);

#if ENABLE_CHUNK_DEBUG
        chkinfo_rack_check(chkinfo);
#endif


        chkinfo->id = *chkid;
        chkinfo->repnum = repnum;
        YASSERT(repnum);

        CHKINFO_DUMP(chkinfo, D_INFO);

        return 0;
err_ret:
        return ret;
}

int __table1_create_table_proto(table1_t *table1, volume_proto_t *volume_proto,
                                const chkid_t *chkid, int is_subvol) {
        int ret;
        chkinfo_t *chkinfo;
        table_proto_t *table_proto;
        char buf[MAX_BUF_LEN];
        char item[FILE_PROTO_ITEM_SIZE];

        chkinfo = (void *)buf;

        ret = __table1_chunk_newinfo(table1, chkid, chkinfo);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = table_proto_create(table1->pool, chkinfo, &volume_proto->chkid, net_getnid(),
                                 TABLE_PROTO_HEAD, NULL, 0, NULL, 0);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        if (is_subvol) {
                memcpy(item, chkinfo, CHKINFO_SIZE(chkinfo->repnum));
                ret = __table1_insert(table1, &table1->parent, chkid, item, CHKINFO_SIZE(chkinfo->repnum));
                if (unlikely(ret)) {
                        table1->ltime = 0;
                        GOTO(err_ret, ret);
                }

                ret = __table1_load_table2(table1, chkinfo, &table_proto, 1, volume_proto);
                if (unlikely(ret)) {
                        table1->ltime = 0;
                        GOTO(err_ret, ret);
                }

                table1->table_array[chkinfo->id.idx] = table_proto; //ugly
        } else {
                // TODO L1 ext

                // FIXME 缓冲区溢出
                // chkstat_t chkstat;
                // memset(&chkstat, 0, CHKSTAT_SIZE(chkinfo->repnum));

                ret = __table1_update_ext_info(table1, chkid, chkinfo, NULL);
                if (unlikely(ret)) {
                        YASSERT(0);
                        table1->ltime = 0;
                        GOTO(err_ret, ret);
                }

                // L1 ext采用和L2 chunk一样的数据布局
                ret = __table1_load_table2(table1, chkinfo, &table_proto, 1, volume_proto);
                if (unlikely(ret)) {
                        YASSERT(0);
                        table1->ltime = 0;
                        GOTO(err_ret, ret);
                }

                table1->ext[chkid->idx - 1] = table_proto;
        }

        return 0;
err_ret:
        return ret;
}

static int __table1_create_l1_ext(table1_t *table1, const chkid_t *top_chkid, volume_proto_t *volume_proto) {
        int ret;

        YASSERT(top_chkid->type == __VOLUME_CHUNK__ && top_chkid->idx <= TABLE1_EXT_MAX_NUM);

        ret = __table1_create_table_proto(table1, volume_proto, top_chkid, 0);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}

int __table1_ensure_l1(table1_t *table1, const chkid_t *tid, volume_proto_t *volume_proto) {
        int ret;
        table_proto_t *table_proto;
        chkid_t top_chkid;

        tid2topid(&top_chkid, tid);

        ret = __table1_get_table_proto_l1(table1, &table_proto, &top_chkid);
        if (unlikely(ret)) {
                // create L1 ext
                if (ret == ENOENT) {
                        ret = __table1_create_l1_ext(table1, &top_chkid, volume_proto);
                        if (unlikely(ret))
                                GOTO(err_ret, ret);
                } else {
                        YASSERT(0);
                }
        }

        return 0;
err_ret:
        return ret;
}
